package cn.hallele.leinsteining.mc.infrastructure.data.converters;

import cn.hallele.leinsteining.mc.infrastructure.data.exceptions.ConverterException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.annotation.Nonnull;
import jakarta.persistence.AttributeConverter;
import lombok.extern.slf4j.Slf4j;

/**
 * json bean 数据库json类型与实体映射转换器
 *
 * @author anle5
 * @since 2024/7/28 14:13
 */
@Slf4j
public class JsonBeanConverter<T> implements AttributeConverter<T, String> {
    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        MAPPER.registerModule(new JavaTimeModule());
    }

    /**
     * 映射类
     */
    private final Class<T> clazz;

    /**
     * 构造函数
     *
     * @param clazz json类型数据映射的目标java类型
     */
    public JsonBeanConverter(@Nonnull Class<T> clazz) {
        this.clazz = clazz;
    }

    /**
     * bean 转json
     *
     * @param attribute the entity attribute value to be converted
     * @return the converted data to be stored in the database
     * column
     */
    @Override
    public String convertToDatabaseColumn(T attribute) {
        return this.toJson(attribute);
    }

    /**
     * json 转bean
     *
     * @param dbData the data from the database column to be
     *               converted
     * @return the converted value to be stored in the entity
     * attribute
     */
    @Override
    public T convertToEntityAttribute(String dbData) {
        return this.toObject(dbData);
    }


    /**
     * object转json string
     *
     * @param object 泛型，对应参数的bean
     * @return string
     */
    private String toJson(T object) {
        try {
            return MAPPER.writeValueAsString(object);
        } catch (Exception e) {
            log.error("Data parse failed, reason: {}.", e.getMessage(), e);
            throw new ConverterException(e);
        }
    }

    /**
     * json string转object
     *
     * @param content content
     * @return 泛型，对应参数的bean
     */
    private T toObject(String content) {
        try {
            return MAPPER.readValue(content, this.clazz);
        } catch (Exception e) {
            log.error("Data parse failed, reason: {}.", e.getMessage(), e);
            throw new ConverterException(e);
        }
    }
}
